﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace evolutionary_algorithm
{
    public class testowy //klasa bedzie przechowywać informacje na temat testowego
    {
        private int index;//index oznacza index który ma odzwierciedlenie w tablicy/liscie z wektorami
        private double waga;//waga oznacza jak wartosciowy jest testowy, im mniejsza tym bardziej wartosciowy
        public testowy(double waga)
        {
            this.waga = waga;
        }
        public int Index
        {
            get { return this.index; }
            set { this.index = value; }
        }
        public double Waga
        {
            get { return this.waga; }
            set { this.waga = value; }
        }

    }
    public class K_NN
    {
        static double[][] vector;
        static int quantity;
        static int ClassAmount;
        static int Radius;
        static double Odleglosc(double[][] wektory, int pierwszy, int drugi)
        {
            //wektory to lista przechowujaca wektory
            //wektory[i] to konkretny wektor
            //wektory[i][j] gdzie j<wektory[i].length to kolejne wartosci x, a ostatnia cyfra to Y
            //pierwszy to numer indeksu pierwszego wektora
            //drugi to numer indeksu drugiego wektora
            /*
             * funkcja bedzie obliczac odleglosc wektora[pierszy] od wektora[drugi]
             * funkcja będzie zwracac właśnie tą odległość
            */
            double wyjscie = 0;//zmienna pomocnicza, sluzy do przechowywania poprzednich wartosci
            for (int i = 0; i < wektory[pierwszy].Length - 1; i++)//petla przechodzi po wszystkich x, nie wchodzi tylko na Y w konretnym wektorze
                wyjscie = Math.Pow(Math.Abs(wektory[pierwszy][i] - wektory[drugi][i]), wektory[pierwszy].Length - 1);//korzystam ze wzoru (x1-x2)^2+(y1-y2)^2... gdzie potega jak i ilosc zmiennych są zmienne
            return Math.Pow(wyjscie, 1 / (double)(wektory[pierwszy].Length - 1)); //gdy otrzymamy juz (x1-x2)^2+(y1-y2)^2... musimy wyciagnac z tego pierwiastek stopnia takiego, ile jest róznych x
        }


        static void ustaw(testowy[] tablica, int testowy, int uczacy, int ilosc, double odleglosc)
        {
            //tablica trzyma wszystkich testujacych oraz posortowanych
            //tablica[i] to grupa testujacych dla uczacego o indeksie i
            //tablica[i][j] to konkretny testujacy dla uczacego
            //tablica[i][j].Index to indeks konkretnego testujacego, który ma odwzwierciedlenie w tablicy/liście z wektorami
            //tablica[i][j].Waga to waga konkretnego testujacego, czyli w naszym przypadku odleglosc uczacego i od testujacego tablica[i][j].Index
            //testowy to indeks testowego, który ma odzwierciedlenie w tablicy/liście z wektorami
            //uczacy to indeks uczacego, który ma odzwierciedlenie w tablicy/liście z wektorami
            //ilosc to informacja jak liczna jest grupa najbliższych dla uczacej, jesli będzie 9, to każdej uczącej zostanie przypisane max 9 testujących
            //odleglosc to odleglosc wektora[testowy] od wektora[uczacy]
            /*
             * funkcja ma za zadanie dopasować testowego do grupy najblizszych uczącego jeśli jest taka możliwość
             * funkcja operuje cały czas na tablicy z testującymi także nic nie zwraca
             */

            if (tablica[ilosc - 1].Waga > odleglosc) //sprawdzam tutaj czy w grupie najbliższych testowych jest choćby jedno miejsce wolny, 
            //moge tak to sprawdzić ponieważ testowe są wpisane do tej tablicy od najbliższego do najdalszego,
            //czyli na ostatnim miejscu jest najdalszy
            {
                for (int i = 0; i < ilosc; i++) //funkcja przechodzi po kazdym testowym w grupie najblizszych
                {
                    if (tablica[i].Waga > odleglosc) // warunek sprawdza czy w miejscu i, nie ma miejsca czasami dla testowego
                    {                                        //jesli warunek sie sprawdzi to bedziemy przesuwac od tego miejsca w tył wszystkie inne testowe, tak aby ten który był ostatni wypadł całkiem z tablicy
                        for (int j = ilosc - 1; j > i; j--)
                        {
                            tablica[j].Index = tablica[j - 1].Index;
                            tablica[j].Waga = tablica[j - 1].Waga;

                        }
                        tablica[i].Index = testowy; //wpisanie w miejscu i indeksu testowego
                        tablica[i].Waga = odleglosc;//wpisanie w miejscu i wagi(odleglosc) testowego
                        break;//break nam przerywa pętle z inkrementacją i, jest to jednoczesnie ostatnia czynność w tej funkcji
                    }

                }
            }
        }


        static testowy[] znajdz(int ilosc_najblizszych, int Index)
        {

            //wektory to lista przechowujaca wszystkie wektory
            //wektory[i] to konkretny wektor
            //wektory[i][j] gdzie j<wektory[i].length to kolejne wartosci dla x w wektorze, ostatnie miejsce dla j to Y
            //punkt_podzialu to informacja o punkcie gdzie znajduja sie uczace<0,punkt_podziau) oraz testujących<punkt_podzialu,wektory.Count)
            //ilosc_najblizszych to informacja o liczności grupy najbliższych wektorów, jesli będzie równa 9, to dla każdego uczącego zostanie przypisanych 9 najbliższych testujących
            /* 
             * funkcja będzie miała za zadanie stworzyć tablice najbliższych oraz przypisać do tablicy najbliższych testujacych dla każdego uczącego
             * funkcja zwraca tablice już utworzoną i wypełnioną testowanymi dla każdego uczącego, jednocześnie tablica jest posortowana tak że każda grupa
             * testowanych dla konkretnej uczącej jest od najbliższego do najdalszego. grupa jest liczności <ilosc_najblizszych>
            */


            testowy[] wyjscie;//tworzymy tablice w której będą zapisane grupy dla każdego uczącego
            wyjscie = new testowy[ilosc_najblizszych]; // tworzymy kolumny w tablicy //tworzymy wiersze w tablicy
            for (int i = 0; i < ilosc_najblizszych; i++)
                wyjscie[i] = new testowy(double.PositiveInfinity);//tworzymy kazdy obiekt po kolei, i przypisujemy mu w wadze nieskonczonosc, im mniejsza waga tym testowy bardziej korzystny.
            //tak więc nieskonczoność jest równoznaczna z brakiem wartości testowanego dla uczacego

            double odleglosc;//zmienna pomocnicza, będzie przechowywać informacje o odległości konkrenego uczacego od konkretnego testowego
            for (int testowy = quantity; testowy < vector.Length; testowy++) // testowy to numer indeksu testowego
            {
                odleglosc = Odleglosc(vector, Index, testowy);//nastepuje przypisanie do zmiennej odleglosc odleglosc miedzy wektorami uczacy i testowy
                ustaw(wyjscie, testowy, Index, ilosc_najblizszych, odleglosc); //funkcja będzie starała się umieścić testowego w grupie najbliższych uczącego o indeksie uczacy
            }


            return wyjscie;//zwraca nam tablice już wypełnioną i posortowaną
        }
        static int tasowanie(double[][] wektory, int podzial)
        {
            //wektory to lista wszystkich wektorów
            //wektory[i] to konkretny wektor o indeksie i
            //wektory[i][j] gdzie j<wektory[i].length to wartości współrzędnych wektora, natomiast na ostatnim miejscu jest Y
            //podzial to informacja o podziale wektórów na uczace<0,podzial) i testujące<podzial,wektory.Count)
            /*
             * funkcja ma za zadanie wybrać z grupy wszystkich wektorów wektory które będą od tego momentu uważane za uczace
             * oraz umieścić je na początku tablicy, w taki sposób że uczace będą miały indeksy <0,punkt) natomiast testowe <punkt, wektory.Count)
             * funkcja zwraca nam informacje o tym gdzie znajduje się punkt podziału na dwie grupy
            */
            double[] wskaznik; //wskaźnik pomocniczy, będzie służył do przeżucania losowych wektorów na początek tablicy
            Random R = new Random();
            int losowa;//zmienna pomocnicza, będzie trzymać wylosowaną liczbę
            for (int i = 0; i < podzial; i++) //pętla będzie chodzić tak długo jak w tablicy nie zostaną wybrane uczace a raczej <podzial> uczacych
            {
                losowa = R.Next(i + 1, wektory.Length); // losowanie liczby z zakresu (i,wektory.Count), to liczba aktualnie dostępnych testowych, jednoczenie wylosowana liczba nie będzie zachaczać o grupe uczących, tylko będzie operować na indeksach testujących
                wskaznik = wektory[losowa];//zamiana wektora losowy z wektorem i, krok 1/3
                wektory[losowa] = wektory[i];//zamiana wektora losowy z wektorem i, krok 2/3
                wektory[i] = wskaznik;//zamiana wektora losowy z wektorem i, krok 3/3
            }
            return podzial; //funkcja zwraca nam informacje o punkcie podzialu
        }
        static int CountClassAmount()
        {//funkcja liczy ile jest roznych klas do klasyfikacji
            bool Test;
            int Y = vector[0].Length - 1;
            List<int> MyList = new List<int>();
            for (int i = 0; i < vector.Length; i++)
            {
                Test = false;
                for (int j = 0; j < MyList.Count; j++)
                    if (vector[i][Y] == MyList[j]) Test = true;
                if (Test == false) MyList.Add(i);
            }
            return MyList.Count;
        }
        static double najczestszy(testowy[][] neighbors, int Index)//
        {
            int[] MostCommonClass = new int[ClassAmount];
            for (int i = 0; i < ClassAmount; i++)
                MostCommonClass[i] = 0;
            for (int j = 0; j < neighbors[Index].Length; j++)
            {
                int k = neighbors[Index][j].Index;//k oznacza indeks testowego dla uczacej o indeksie i
                int l = Convert.ToInt32(vector[k][vector[k].Length - 1]);//l oznacza Y testowego
                MostCommonClass[l - 1]++;//licznik ktory Y w danej grupie testowych
            }
            //bede teraz wybieral ktory element w zbiorze najczestszych jest najliczniejszy, czyli ktorych Y jest najwiecej
            int max_i = 0;
            int max = MostCommonClass[0];
            for (int j = 0; j < ClassAmount; j++)
                if (MostCommonClass[j] > max)
                {
                    max_i = j;
                    max = MostCommonClass[j];
                }
            return max_i + 1;
        }


        public K_NN(string path)//otwiera plik i zczytuje dane(potem i tak bedzie baza danych sql)
        {
            string[] AlLines = File.ReadAllLines(path);
            vector = new double[AlLines.Length][];
            for (int y = 0; y < AlLines.Length; y++)
            {
                string[] OneLine = AlLines[y].Split(new string[] { " " },
                       StringSplitOptions.RemoveEmptyEntries);
                vector[y] = new double[OneLine.Length];
                for (int x = 0; x < OneLine.Length; x++)
                    vector[y][x] = Convert.ToDouble(OneLine[x]);
            }
            ClassAmount = CountClassAmount();
            quantity = 0;
        }
        public double Create(double percent, int radius) //tworzy liste dokladnosci dla danego procenta, procent oznacza ilsoc uczacych 
        {
            quantity = Convert.ToInt32(vector.Length * percent / 100);
            tasowanie(vector, quantity);
            double precision = 0;
            testowy[][] wyjscie = new testowy[quantity][];
            for (int i = 0; i < quantity; i++)
                wyjscie[i] = znajdz(radius, i);//glowna funkcja, znajdujemy oraz przyporzadkowujemy <ilosc_najblizszych> testowych
            for (int i = 0; i < quantity; i++)
                if (najczestszy(wyjscie, i) == vector[i][vector[i].Length - 1]) precision++;
            precision /= quantity;
            Radius = radius;
            return precision;
        }
        public double[] CreateTable(double[,] AG_Table)
        {
            double[] precision = new double[AG_Table.GetLength(0)];
            quantity = AG_Table.GetLength(1);
            testowy[][] Handler = new testowy[quantity][];
            int Length;
            for (int i = 0; i < AG_Table.GetLength(0); i++)
            {
                Length = 0;
                for (int j = 0; j < AG_Table.GetLength(1); j++)
                    if (AG_Table[i, j] == 1)
                    {
                        Handler[Length] = znajdz(Radius, j);
                        Length++;
                    }
                for (int j = 0; j < Length; j++)
                    if (najczestszy(Handler, j) == vector[j][vector[i].Length - 1]) precision[i]++;
                precision[i] /= Length;

            }
            return precision;
        }
    }
}
